//前言    进阶模版的重点有三个
//非类型模版参数
//类模版的特化
//模版的分离编译



//一、非类型模版参数
//模板类型参数分为类型模板参数，和非类型模板参数
//类型模板参数：出现在模板参数列表当中，跟在class或者typename之类的参数类型名称
//非类型模板参数：就是用一个常量作为类（函数）模板的一个参数，在类（函数）模板中可将该参数当做常量来使用


//1.虚拟类型
//template<class T>//这个T就是虚拟类型
////因为它不是一个实际的类型，实例化之后才是一个真正的类型
////但是模板参数并不一定是虚拟类型参数       还有可能是----非类型模板参数
//class test
//{
//    ;
//};





//2.非类型模板参数的简单使用
//#include<iostream>
//using namespace std;

//template<class T,size_t N=100>         //这个N就是非类型模板参数-----要记住，非类型模板参数定义的是一个常量
////ps:这个非类型模板参数一定要是一个整形类型的常量，也就是说    这个N一定要代表的是整型，不能是float，double，int*之类的常量
////模板类型中也可以单独使用这个非类型模板参数  template<size_t N=100>   这样去使用
//class mystack
//{
//private:
//    T arr[N];//在定义的时候就是知道需要多大的变量的空间   //并且还是可以使用缺省参数,缺省的用法和函数的缺省参数是一样的
//    size_t top;
//
//};
//
//
//void test1()
//{
//    mystack<int>s1;//不写就是默认为100个容量的栈
//    mystack<int,200>s2;//这就是非类型参数的简单使用场景，定义了个200容量的栈
//}
//
//
//int main(void)
//{
//  test1();
//}




//3.使用了非类型模板参数的库中的容器 -------array
//template < class T, size_t N > class array;   //库中的具体定义

//#include<array>
//
//int main(void)
//{
//    int arr1[10];
//    array<int,10>arr2;
//    //这两个在本质上是一样的,只是说arr2是一个对象,另外一个是一个数组
//
//    cout<<sizeof(arr1)<<endl;
//    cout<<sizeof(arr2)<<endl;//这两个占用的内存是一样大的
//    //但是实际上使用起来array并没有什么大的用处
//    //唯一的优势就是对边界的检查更加严格
//}












//二丶模板的特化
//通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果
//1.对于函数而言
//template<class T>
//bool less(const T& left, const T& right)//这个地方叫做参数匹配   -----官方一点的说法就是,模板的特化
////函数的参数匹配是会找最合适的,如果有指定的int left,int right会优先使用这个函数
//{
//    return left<right;
//}



//2.函数模板特化
#include"23_priority_queue的模拟.h"
using namespace llk;

template<class T>
bool objless(const T& left,const T& right)//这个地方叫做参数匹配   -----官方一点的说法就是,模板的特化
//函数的参数匹配是会找最合适的,如果有指定的int left,int right会优先使用这个函数
{
    return left < right;
}

template<>
bool objless<const Date*&>(const Date*& left,const Date*& right)//这种实现的形式叫做模板的特化   (其他都是模板,就这个类型的比较需要单独实例化出来,比较特殊,特化)
{
    return *left<*right;
}//在函数模板里面,特化很鸡肋,还不如写一个函数的重载


//int main(void)
//{
//    //普通的内置类型当然可以直接进行大小的比较
//    cout<<objless(1,2)<<endl;
//
//    //但是对象指针的比较就会出现错误,因为会直接比较指针
//    Date* a1=new Date(1000,1,1);
//    Date* a2=new Date(1000,1,2);
//    cout<<objless(a1,a2)<<endl;//这样比较的仅仅是指针地址的大小
//}
//补充:
//特化的前提条件是有一个基础的函数模板
//关键字template后面只有一个<>
//函数名后面需要用<类型>,也就是需要特化的类型
//特化函数函数里面的形参列表应该基础模板完全相同

















//三.类模板特化
//1.全特化
//全特化是将模板参数列表中所有参数都指定     ----举个例子
//template<class T1,class T2>//这个只是泛型,不管什么样的俩类型都行
//class test
//{
//public:
//    test()
//    {
//        cout<<"这就是个构造函数"<<endl;
//    }
//private:
//    T1 tt1;
//    T2 tt2;
//};
//
//
//template<>//这样的特化方式就是类模板的特化,并且因为参数模板里面有两个参数,全部都更改了,这也叫全特化
//class test<int,char>
//{
//public:
//    test()
//    {
//        cout<<"这就是个构造函数"<<endl;
//    }
//private:
//    int tt1;
//    char tt2;
//};
//也就是针对一些特殊的制定参数才会去使用




//2.偏特化(也叫半特化)
//template<class T1,class T2>
//class test
//{
//public:
//    test()
//    {
//        cout<<"这就是个构造函数"<<endl;
//    }
//private:
//    T1 tt1;
//    T2 tt2;
//};
//
//
//template<>
//class test<int,char>//全特化的例子
//{
//    //.....内容和上面相同
//};
//
//template<class T1>//偏特化的例子      -----偏特化上面的这个模板参数只需要写出来一个就好
//class test<char,T1>//这就是偏特化的例子//T1和char的位置可以交换   ,但是模板交换的话,下面实例化的时候也需要进行响应的交换
//{
//    //.....内容和上面相同
//};
//
//
//
//
////并且偏特化还有一种特殊的
//template<class T1,class T2>
//class test<T1*,T2*>//只要T1和T2是指针类型的就会调用这个类
//{
//    //.....内容和上面相同
//};
//
//
//template<class T1,class T2>
//class test<T1&,T2&>//
//{
//    //......内容和上面的相同
//};
//
//
//
//
////int main(void)
////{
////    test<char,double> d1;//虽然有偏特化,但是还是要补需要传入的类型显示的写出来,否则会报错----也就是不能test<double>d1这样去定义   这样是错误的
////
////}
////总结:
////全特化也就是模板参数在类的实例化过程中都给定了指定的类型
////半特化将模板参数确定一部分类型,或者是对模板参数进行更一步的限制
//
//
//
//
//template<class T1,size_t N>
//class A
//{
//    ;
//};
//
//template<class T1>
//class A<T1,20>
//{
//    ;
//};
//非类型模板参数也能进行特化


//后续在哈希表中还会有进一步的重载














//四.模板分离编译
//首先清楚什么是分离编译:一个程序(项目)若是由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后讲所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式


//1.模板的分离编译
//首先明白定义和声明分离是为了维护方便
//详细见24_模板的分离编译test
//#include"24_模板的分离编译.h"

//int main(void)
//{
//    sub(1,2);//普通的函数定义和声明是可以进行调用的         因为这个函数的定义和声明是分开的,然后汇编代码中地址是在链接的时候确定的
//    add<double>(1.1,2.2);//理论上会去调用模板函数,但是实际上会在链接的时候进行报错
//    //但是如果这个定义和声明不分离,那么这个地址就是在编译的时候就确定了
//}//25节课 1:00:00-1:30:00   详细的讲解








//五.模板总结
//模板的优点:
// 模板复用了代码,节省资源,更快的迭代开发,c++的标准模板库(stl)因此产生
//增强了代码的灵活性


//模板的缺陷:
//模板会导致代码膨胀问题,也会导致编译时间变长               -----代码膨胀问题就是在你传不同的类型,就会吃生成不同的类型的函数(类),也就是生成了多分
//但是这个也不算缺陷,因为本来也要写这么多的实例化的函数,时间边长就是因为实例化的原因

//出现模板编译错误时,错误信息非常乱,不容易定位错误